Skip to content

Symbol#source correct for all phases #6757

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

milessabin
Copy link
Contributor

@milessabin milessabin commented Jun 26, 2019

A combination of the inliner missing opportunities for setting defTrees with the correct inlined source information and some suspect post erasure logic in Symbol#source led to the same-source check in
ExpandPrivate failing in joint-compilation scenarios resulting in multi-file programs being incorrectly rejected for access violations.

The inliner duplicates trees and their symbols when expanding inline definitions at call sites. When the RHS of the inline definition contains a class definition its Symbol is duplicated, I believe correctly, preserving the original position and associated source file. The tree is duplicated, I believe also correctly, with its source attribute reflecting that the tree has been expanded in the call site source file.

In most circumstances the potential mismatch between the source of the Symbol and the source of its defining tree is handled: in Symbol#source the source of the defining tree will be used prior to erasure if the tree is non-empty, which ensures that the source as viewed via the Symbol will be the same as the source as viewed via the tree.

Things can go awry after erasure, however. In ExpandPrivate there is a same-source-file test relaxing access controls to private members of module classes. Because this happens after erasure it's possible that if a class definition has been inlined into a module and members attempt to access a definition in the module (eg. something synthetic which has been lifted into the module) the same-source-file test will fail because the source of the class symbol will now be seen as the source of the inline definition rather than the source of the inlined call site.

This is the scenario exercised in the included test. The inlined refinement and by-name argument are both essential (the latter is responsible for the private synthetic term which is lifted out into the module and then incorrectly reported as inaccessible from the inlined body). Also note that multiple source files are essential, but that separate compilation (as opposed to joint compilation) masks the issue which is why this problem didn't show up for the multi-file version of my type class derivation prototype.

In the real code from which this was reduced the refinement was actually the expansion of a polymorphic function type, but as the test shows there's nothing specific to polymorphic function types here.

The simplest approach to fixing this appears to be in ExpandPrivate where if the accessing symbol (on the context.owner side) doesn't have a matching source file we fall back to checking against the source file of the current compilation unit.

def check(src: SourceFile): Boolean =
src.exists && isSimilar(dPath, src.path)

assert(d.symbol.source.exists && (check(ctx.owner.source) || check(ctx.compilationUnit.source)),
s"private ${d.symbol.showLocated} in ${d.symbol.source} accessed from ${ctx.owner.showLocated} in ${ctx.owner.source}")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this not be always ctx.compilationUnit.source? For the purposes of the bytecode the current owner does does not matter, I think. Have you tried to simply change ctx.owner.source to ctx.compilationUnit.source?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did try that initially. It doesn't work because if there's inlining on the d side then post-erasure the source of the symbol might be different from the compilation unit.

Copy link
Contributor Author

@milessabin milessabin Jun 27, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An alternative might be to check d.defTree.source against ctx.compilationUnit.source ... does that sound reasonable?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

defTree does not always exist. So, I don't see a way to simplify the current state either.

@odersky odersky assigned milessabin and unassigned odersky Jun 27, 2019
@milessabin
Copy link
Contributor Author

milessabin commented Jul 1, 2019

@odersky I wasn't really happy with the PR as it stood, so I did some more digging. It turns out that the inliner was missing the opportunity to assign defTrees for newly created inline symbols ... this appears to be responsible for at least some of the mismatches on the d-side of the ensurePrivateAccessible check.

Also, we can't always use ctx.compilationUnit.source because, at least in the fromTasty tests, this can end up being a class file if the top level entity is an outer class. In principle we might hit this situation (passing from inner source to an outer class file) on both sides of the test as we ascend the owner chains.

I've tweaked the inliner to set defTrees where possible, and I've also pulled the source file computation out (findSource) and applied it on both sides of the test: arguably this is what the post-erasure implementation of Symbol#source should actually be ... I'd be happy to try moving this logic there if you think that's the right way to go.

@milessabin milessabin assigned odersky and unassigned milessabin Jul 1, 2019
@milessabin
Copy link
Contributor Author

I've moved the findSource logic I previously had in ExpandPrivate to Symbol#source which means that the original bug this was intended to fix is now fixed without any changes needed to ExpandPrivate.

@odersky this feels like a better and more principled fix ... WDYT?

@milessabin milessabin changed the title Fix ExpandPrivate interaction with inlining Symbol#source correct for all phases Jul 3, 2019
@milessabin milessabin requested a review from nicolasstucki July 3, 2019 14:07
@milessabin milessabin assigned nicolasstucki and unassigned odersky Jul 3, 2019
@milessabin
Copy link
Contributor Author

@nicolasstucki thanks ... I'll squash and merged :-)

A combination of the inliner missing opportunities for setting defTrees
with the correct inlined source information and some suspect post
erasure logic in Symbol#source led to the same-source check in
ExpandPrivate failing in joint-compilation scenarios resulting in
multi-file programs being incorrectly rejected for access violations.
@milessabin milessabin force-pushed the topic/inline-refinement-by-name branch from 3718405 to cf44e91 Compare July 3, 2019 14:55
@milessabin
Copy link
Contributor Author

Squashed and rebased ... I'll merge when it goes green.

@milessabin milessabin merged commit 28cf0cb into scala:master Jul 3, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants